#ifndef RenderModel_h__
#define RenderModel_h__

#include "Material.h"

class DrawVert {
public:
    enum { NUM_LIMITS_ST =2 };

    inf::Vec3			xyz;
    inf::Vec2			st[2];
    inf::Vec3			normal;
    inf::Vec3			tangents[2];
    inf::byte			color[4];

    float			operator[]( const int index ) const;
    float &			operator[]( const int index );

    void			Clear( void );

    void			Lerp( const DrawVert &a, const DrawVert &b, const float f );
    void			LerpAll( const DrawVert &a, const DrawVert &b, const float f );

    void			Normalize( void );

    void			SetColor( inf::dword color );
    inf::dword		GetColor( void ) const;
};

INF_INLINE float DrawVert::operator[]( const int index ) const {
    assert( index >= 0 && index < xyz.get_dimension()+st[0].get_dimension()*NUM_LIMITS_ST );
    return ((float *)(&xyz))[index];
}
INF_INLINE float	&DrawVert::operator[]( const int index ) {
    assert( index >= 0 && index < xyz.get_dimension()+st[0].get_dimension()*NUM_LIMITS_ST );
    return ((float *)(&xyz))[index];
}

INF_INLINE void DrawVert::Clear( void ) {
    xyz.zero();
    for( int i=0; i<NUM_LIMITS_ST; ++i )
    {
        st[i].zero();
    }
    normal.zero();
    tangents[0].zero();
    tangents[1].zero();
    color[0] = color[1] = color[2] = color[3] = 0;
}

INF_INLINE void DrawVert::Lerp( const DrawVert &a, const DrawVert &b, const float f ) {
    xyz = a.xyz + f * ( b.xyz - a.xyz );
    for( int i=0; i<NUM_LIMITS_ST; ++i ) {
        st[i] = a.st[i] + f * ( b.st[i] - a.st[i] );
    }
}

INF_INLINE void DrawVert::LerpAll( const DrawVert &a, const DrawVert &b, const float f ) {
    xyz = a.xyz + f * ( b.xyz - a.xyz );
    for( int i=0; i<NUM_LIMITS_ST; ++i ) {
        st[i] = a.st[i] + f * ( b.st[i] - a.st[i] );
    }
    normal = a.normal + f * ( b.normal - a.normal );
    tangents[0] = a.tangents[0] + f * ( b.tangents[0] - a.tangents[0] );
    tangents[1] = a.tangents[1] + f * ( b.tangents[1] - a.tangents[1] );
    color[0] = (inf::byte)( a.color[0] + f * ( b.color[0] - a.color[0] ) );
    color[1] = (inf::byte)( a.color[1] + f * ( b.color[1] - a.color[1] ) );
    color[2] = (inf::byte)( a.color[2] + f * ( b.color[2] - a.color[2] ) );
    color[3] = (inf::byte)( a.color[3] + f * ( b.color[3] - a.color[3] ) );
}

INF_INLINE void DrawVert::SetColor( inf::dword color ) {
    *reinterpret_cast<inf::dword *>(this->color) = color;
}

INF_INLINE inf::dword DrawVert::GetColor( void ) const {
    return *reinterpret_cast<const inf::dword *>(this->color);
}

typedef unsigned short index_t;

typedef struct srfTriangles_s {
    inf::Bounds     bounding;

    int             numVerts;
    DrawVert*       verts;

    int             numIndexes;
    index_t*        indexes;
} srfTriangles_t;

typedef struct modelSurface_s {
    int             id;
    Material*       shader;
    srfTriangles_t* geometry;
} modelSurface_t;

typedef enum {
    GT_TRIANGLE,
    GT_CUBE
} genType_t;

struct renderEntity_s;

class RenderModel {
public:
    virtual ~RenderModel() {}

    virtual void            InitFromFile( const char* fileName ) = 0;
    virtual const char*     GetName() const = 0;
    virtual void            TouchData() = 0;

    virtual bool            IsLoaded() const = 0;
    virtual void            LoadModel() = 0;

    virtual void            PurgeModel() = 0;

    virtual void            AddSurface( modelSurface_t& surface ) = 0;
    virtual void            FinishSurfaces() = 0;

    virtual int             NumSurfaces() const = 0;
    virtual modelSurface_t* GetSurface( int index ) = 0;

    virtual srfTriangles_t* AllocSurfaceTriangles( int numVerts, int numIndexes ) = 0;
    virtual void            FreeSurfaceTriangles( srfTriangles_t* srf ) = 0;

    virtual inf::Bounds     Bounds( const renderEntity_s* ent = NULL ) = 0;
};

#endif // RenderModel_h__